# -*- coding: utf-8 -*-

import os
import struct
import sys
import time

__author__ = 'feifei'

if sys.version_info > (3, 0):
    def get_record_content(buf, start_offset):
        end_offset = buf.find(b'\x00', start_offset)
        # 省份|城市
        pc_string = buf[start_offset:end_offset].decode().split('|')
        return "%s|%s" % (pc_string[0], pc_string[1])
else:
    def get_record_content(buf, start_offset):
        end_offset = buf.find('\x00', start_offset)
        pc_string = buf[start_offset:end_offset].decode().split('|')
        return "%s|%s" % (pc_string[0], pc_string[1])


class Phone(object):
    def __init__(self):

        self.src_phone_path = "SRC"
        self.bak_phone_path = "bak"
        dat_file = self.src_phone_path + "/phone.dat"

        with open(dat_file, 'rb') as f:
            self.buf = f.read()
        self.head_fmt = "<4si"
        self.phone_fmt = "<iiB"
        self.head_fmt_length = struct.calcsize(self.head_fmt)
        self.phone_fmt_length = struct.calcsize(self.phone_fmt)
        self.version, self.first_phone_record_offset = struct.unpack(
            self.head_fmt, self.buf[:self.head_fmt_length])
        self.phone_record_count = (len(
            self.buf) - self.first_phone_record_offset) // self.phone_fmt_length
        self.first_phone_offset = struct.unpack(
            self.head_fmt, self.buf[:self.head_fmt_length])[1]
        self.city_dict = {}
        start_offset = self.head_fmt_length
        while start_offset < self.first_phone_record_offset:
            end_offset = self.buf.find(b'\x00', start_offset)
            province = self.buf[start_offset:end_offset].decode().split('|')[0]
            city = self.buf[start_offset:end_offset].decode().split('|')[1]
            if self.city_dict.get(province):
                self.city_dict[province][city] = start_offset
            else:
                self.city_dict[province] = {}
                self.city_dict[province][city] = start_offset
            start_offset = end_offset + 1

    def get_phone_dat_msg(self):
        print("版本号:{}".format(self.version))
        print(self.first_phone_record_offset)
        print("总记录条数:{}".format(self.phone_record_count))

    @staticmethod
    def get_phone_no_type(no):
        if no == 7:
            return "联通虚拟运营商"
        if no == 6:
            return "移动虚拟运营商"
        if no == 5:
            return "电信虚拟运营商"
        if no == 4:
            return "虚拟运营商"
        if no == 3:
            return "电信"
        if no == 2:
            return "联通"
        if no == 1:
            return "移动"

    def create_bak(self):
        if not os.path.exists(self.bak_phone_path):
            os.mkdir(self.bak_phone_path)
        now_ymd = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
        bak_file_name = 'phone.dat.' + now_ymd
        with open("%s/%s" % (self.bak_phone_path, bak_file_name), 'wb') as f:
            f.write(self.buf)
        # 先更改原文件名称，验证成功后，删除该文件
        # os.rename("%s/phone.dat" % self.src_phone_path, "%s/phone.dat.1" % self.src_phone_path)
        return self.bak_phone_path + '/' + bak_file_name

    @staticmethod
    def _format_phone_content(phone_num, record_content, phone_type, offset):

        province = record_content.split('|')[0]
        city = record_content.split('|')[1]
        return {
            "phone": phone_num,
            "province": province,
            "city": city,
            "phone_type": Phone.get_phone_no_type(phone_type),
            "offset": offset

        }

    def _lookup_phone(self, buf, phone_num):

        int_phone = int(phone_num)
        version, first_phone_record_offset = struct.unpack(
            self.head_fmt, buf[:self.head_fmt_length])

        phone_record_count = (len(
            buf) - first_phone_record_offset) // self.phone_fmt_length
        left = 0
        right = phone_record_count
        buflen = len(buf)
        while left <= right:
            middle = (left + right) // 2
            current_offset = (first_phone_record_offset +
                              middle * self.phone_fmt_length)
            if current_offset >= buflen:
                return ""
            buffer = buf[current_offset: current_offset + self.phone_fmt_length]
            cur_phone, record_offset, phone_type = struct.unpack(self.phone_fmt,
                                                                 buffer)

            if cur_phone > int_phone:
                right = middle - 1
            elif cur_phone < int_phone:
                left = middle + 1
            else:
                record_content = get_record_content(buf, record_offset)
                return self._format_phone_content(phone_num, record_content,
                                                  phone_type, current_offset)

    def find(self, phone_num, buf=None):
        str_num = str(phone_num)
        # assert 7 <= len(str_num) <= 16
        if buf is None:
            buf = self.buf
        num9 = str_num[:9]
        out_str9 = self._lookup_phone(buf, num9)
        if out_str9:
            return out_str9
        num8 = str_num[:8]
        out_str8 = self._lookup_phone(buf, num8)
        if out_str8:
            return out_str8
        num7 = str_num[:7]
        out_str7 = self._lookup_phone(buf, num7)
        if out_str7:
            return out_str7

    def add_phone(self, str_phone_num, province, city, phone_type_no):
        # 读取要添加的号段信息  号码|省份|城市|运营商
        # 逐个添加
        bak_file_name = self.create_bak()
        buffer = self.buf
        int_phone_num = int(str_phone_num)
        # 获取第一个号码及最后一个号码，判断新增号码的位置
        phone_offset = self.city_dict[province][city]
        first_num_buf = buffer[self.first_phone_offset:self.first_phone_offset + self.phone_fmt_length]
        first_num = struct.unpack(self.phone_fmt, first_num_buf)[0]
        buflen = len(buffer)
        last_num_buf = buffer[buflen - self.phone_fmt_length:buflen]
        last_num = struct.unpack(self.phone_fmt, last_num_buf)[0]
        # print(first_num, last_num)
        outputdata = struct.pack(self.phone_fmt, int_phone_num, int(phone_offset), phone_type_no)
        # print(outputdata)
        # 生成备份文件
        if int_phone_num < first_num:
            new_buf = buffer[:self.first_phone_offset] + outputdata + buffer[self.first_phone_offset:]
        elif int_phone_num > last_num:
            new_buf = buffer + outputdata
        else:
            num = 0
            while not self.find(int_phone_num - num, buffer):
                num += 1
            cur_offset = self.find(int_phone_num - num, buffer)['offset']
            # print(self.find(int_phone_num - num, buffer))
            new_buf = buffer[:cur_offset + 9] + outputdata + buffer[cur_offset + 9:]
            # print(self.find(int_phone_num, new_buf))
            # a, b, c = struct.unpack(self.phone_fmt, buffer[cur_offset:cur_offset + self.phone_fmt_length])
        if self.find(int_phone_num, new_buf):
            buffer = new_buf
        else:
            return None
        os.remove("%s/phone.dat" % self.src_phone_path)
        with open("%s/phone.dat" % self.src_phone_path, 'wb') as fd:
            fd.write(buffer)
        return bak_file_name

    def delete_phone(self, phone_num):
        bak_file_name = self.create_bak()
        buffer = self.buf
        phone_dict = self.find(phone_num, buffer)
        now_offset = phone_dict['offset']
        new_buf = buffer[:now_offset] + buffer[now_offset + 9:]
        if self.find(phone_num, new_buf):
            return None
        os.remove("%s/phone.dat" % self.src_phone_path)
        with open("%s/phone.dat" % self.src_phone_path, 'wb') as fd:
            fd.write(new_buf)
        return bak_file_name

    def alter_phone(self, phone_num, province, city, phone_type_no):
        buffer = self.buf
        bak_file_name = self.create_bak()
        pc_offset = self.city_dict[province][city]
        outline = struct.pack(self.phone_fmt, int(phone_num), int(pc_offset), phone_type_no)
        phone_list = self.find(int(phone_num), buffer)
        cur_offset = phone_list['offset']
        # print(pc_offset)
        # print(cur_offset)
        new_buf = buffer[:cur_offset] + outline + buffer[cur_offset + 9:]
        os.remove("%s/phone.dat" % self.src_phone_path)
        with open("%s/phone.dat" % self.src_phone_path, 'wb') as fd:
            fd.write(new_buf)
        return bak_file_name

    def read_phone(self, p, c, t):
        # phone_list = []
        phone_dict = {}
        current = self.first_phone_record_offset
        while current + self.phone_fmt_length < len(self.buf):
            phone_num, record_offset, phone_type = struct.unpack(self.phone_fmt,
                                                                 self.buf[current:current + self.phone_fmt_length])
            current += self.phone_fmt_length
            record_content = get_record_content(self.buf, record_offset)
            province, city = record_content.split('|')
            p1 = province if p == '不限' else p
            c1 = city if c == '不限' else c
            t1 = phone_type if t == '不限' else t
            if p1 == province and c1 == city and t1 == phone_type:
                phone_data = Phone._format_phone_content(phone_num, record_content, phone_type, record_offset)
                phone_dict[phone_num] = phone_data
        return phone_dict

    @staticmethod
    def human_phone_info(phone_info):
        if not phone_info:
            return ''
        return "{}|{}|{}|{}".format(phone_info['phone'],
                                    phone_info['province'],
                                    phone_info['city'],
                                    # phone_info['zip_code'],
                                    # phone_info['area_code'],
                                    phone_info['phone_type'])

    # def check_input(self, phone_num):
    #     if phone_num.isnumeric():
    #         str_phone_num = str(phone_num)
    #         int_phone_num = int(phone_num)
    #         if len(str_phone_num) < 7:
    #             return [0, "号码小于7位，请检查"]
    #         elif len(str_phone_num) > 16:
    #             return [0, "号码过长，请检查"]
    #         else:
    #             return [1, int_phone_num]
    #     else:
    #         return [0, "包含非数字"]

    def test(self):
        self.get_phone_dat_msg()
        for i in range(101449999, 101450008):
            print(self.human_phone_info(self.find(i)))


if __name__ == "__main__":
    phone = Phone()
    phone.test()
